package cn.springboot.erp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.MD5;
import cn.springboot.erp.commons.QueryParams;
import cn.springboot.erp.entity.*;
import cn.springboot.erp.form.LoginForm;
import cn.springboot.erp.form.UserForm;
import cn.springboot.erp.mapper.*;
import cn.springboot.erp.utils.IpUtil;
import cn.springboot.erp.utils.JWTUtil;
import cn.springboot.erp.commons.ResponseResult;
import cn.springboot.erp.vo.UserVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.BeanUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.springboot.erp.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author asus
* @description 针对表【user】的数据库操作Service实现
* @createDate 2024-04-29 19:10:54
*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService{

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private JWTUtil jwtUtil;

    @Resource
    private UserMapper userMapper;
    @Resource
    private DeptMapper deptMapper;
    @Resource
    private UserRoleMapper userRoleMapper;
    @Resource
    private RoleMapper roleMapper;
    @Resource
    private RolePersMapper rolePersMapper;
    @Resource
    private PermissionMapper permissionMapper;

    @Override
    public ResponseResult login(LoginForm loginForm, HttpServletRequest request) {
        /**
         * 1、比较验证码是否正确
         *  "redis"中的验证码是否过期
         *  "loginForm"中的验证码与"redis"中的验证码比较
         */
        // 获取"loginForm"中的验证码"code"
        String formCode = loginForm.getCode();
        // 从"loginForm"中获取"redis"缓存中存储验证码的key"uuid"
        String uuid = loginForm.getUuid();
        // 根据"uuid"从"redis"中获取验证码
        String redisCode = stringRedisTemplate.opsForValue().get(uuid);
        // 判断"redis"缓存中的验证码是否过期
        if (StringUtils.isEmpty(redisCode)){
            return ResponseResult.Fail().msg("验证码过期");
        }
        // 判断"loginForm"中的验证码与"redis"缓存中的验证码是否相同
        if (!ObjectUtil.equal(formCode,redisCode)){
            return ResponseResult.Fail().msg("验证码错误，请填写正确的验证码");
        }

        /**
         * 2、校验用户名和密码
         *  根据"logForm"中的用户名查询数据库
         *  用户名是否存在
         *  密码是否正确
         *  状态是否正常
         */
        // 获取"loginForm"中的"username"
        String username = loginForm.getUsername();
        // 根据"username"查询数据库
        LambdaQueryWrapper<User> lambda = new QueryWrapper<User>().lambda();
        lambda.eq(User::getUsername,username);
        User user = userMapper.selectOne(lambda);

        // 判断用户信息是否存在
        if (ObjectUtils.isNull(user)){
            return ResponseResult.Fail().msg("用户名不存在");
        }
        // 获取"loginForm"中的密码
        String formPassword = loginForm.getPassword();
        // 对"formPassword"进行MD5加密处理
        String md5Password = new Md5Hash(formPassword, user.getSalt(), 1024).toHex();
        // 获取"user"对象中的密码
        String password = user.getPassword();
        // 比较密码是否相同
        if (!ObjectUtil.equal(md5Password,password)){
            return ResponseResult.Fail().msg("密码错误");
        }
        // 判断用户状态是否正常
        if (user.getState()==0){
            return ResponseResult.Fail().msg("用户被禁用，请联系管理员");
        }

        /**
         * 将用户信息封装成token
         */
        // 创建载荷信息
        HashMap<String, Object> payload = new HashMap<>();
        payload.put("username",user.getUsername());
        payload.put("id",user.getId());
        // 创建token
        String token = jwtUtil.createToken(payload);
        // 将"token"进行加密
        String keyToken = MD5.create().digestHex16(token);
        // 获取ip地址
        String valueIp = IpUtil.getIp(request);
        // 将加密后的"keyToken"作为"key","valueIp"作为"value"缓存到redis中
        // 防止"token"被盗用
        stringRedisTemplate.opsForValue().set(keyToken,valueIp,7, TimeUnit.DAYS);
        // 返回响应信息"token"和"用户昵称"
        return ResponseResult.Success().msg("登录成功")
                .data("token",token)
                .data("nickName",user.getNickName());
    }

    @Override
    public ResponseResult userPage(QueryParams<User> queryParams) {
        // 获取分页数据
        Integer page = queryParams.getPage();
        Integer limit = queryParams.getLimit();
        User searchParams = queryParams.getSearchParams();

        // 执行分页操作
        Page<User> userPage = new Page<>(page,limit);
        LambdaQueryWrapper<User> lambda = new QueryWrapper<User>().lambda();
        lambda.eq(StringUtils.isNotBlank(searchParams.getUsername()),User::getUsername,searchParams.getUsername())
                .eq(ObjectUtils.isNotNull(searchParams.getDeptId()),User::getDeptId,searchParams.getDeptId())
                .orderByDesc(User::getCreateTime);
        baseMapper.selectPage(userPage,lambda);

        // 获取数据总条数

        long total = userPage.getTotal();

        // 获取分页数据
        List<User> records = userPage.getRecords();

        List<UserVO> userVOS = records.stream().map(user -> {
            UserVO userVO = new UserVO();
            BeanUtil.copyProperties(user, userVO);

            // 根据部门编号查询部门
            // 获取部门id
            Integer deptId = user.getDeptId();
            LambdaQueryWrapper<Dept> deptLambda = new QueryWrapper<Dept>().lambda();
            deptLambda.eq(Dept::getId,deptId);
            Dept dept = deptMapper.selectOne(deptLambda);
            userVO.setDeptName(dept.getDeptName());

            // 根据用户id查询角色名称
            // 根据用户id查询角色id
            Integer uid = user.getId();
            LambdaQueryWrapper<UserRole> userRoleLambda = new QueryWrapper<UserRole>().lambda();
            userRoleLambda.eq(UserRole::getUserId,uid);
            List<Integer> roleIds = userRoleMapper.selectList(userRoleLambda)
                    .stream()
                    .map(userRole -> userRole.getRoleId())
                    .collect(Collectors.toList());

            // 根据角色id查询角色名称
            List<String> roleName = roleMapper.selectBatchIds(roleIds).stream().map(role -> role.getRoleName()).collect(Collectors.toList());
            userVO.setRoleName(roleName);
            return userVO;
        }).collect(Collectors.toList());
        System.out.println("userVOS = " + userVOS);
        return ResponseResult.Success().data("total",total).data("tableData",userVOS);
    }

    @Override
    public ResponseResult selectById(Integer id) {
        UserForm userForm = new UserForm();

        // 根据id查询用户
        User user = baseMapper.selectById(id);
        //将用户信息复制到userForm
        BeanUtil.copyProperties(user,userForm);
        // 根据角色id查询用户角色关联表
        LambdaQueryWrapper<UserRole> lambda = new QueryWrapper<UserRole>().lambda();
        lambda.eq(UserRole::getUserId ,user.getId());
        List<Integer> roleIds = userRoleMapper.selectList(lambda).stream().map(userRole -> userRole.getRoleId()).collect(Collectors.toList());
        userForm.setRoleId(roleIds);
        return ResponseResult.Success().data("userForm",userForm);
    }

    @Override
    public ResponseResult insertUser(UserForm userForm) {
        // 获取用户名
        String username = userForm.getUsername();
        // 根据用户名查询
        LambdaQueryWrapper<User> lambda = new QueryWrapper<User>().lambda();
        lambda.eq(User::getUsername,username);
        User user = baseMapper.selectOne(lambda);
        if (ObjectUtils.isNotNull(user)){
            return ResponseResult.Fail().msg("用户名已经存在");
        }

        User userOne = new User();
        BeanUtil.copyProperties(userForm,userOne);

        // 设置密码和盐
        String defaultPassword = "111111";
        String salt = RandomUtil.randomString(6);
        String md5Password = new Md5Hash(defaultPassword, salt, 1024).toHex();
        userOne.setPassword(md5Password);
        userOne.setSalt(salt);

        // 添加用户信息
        baseMapper.insert(userOne);

        // 添加角色信息
        Integer id = userOne.getId();
        List<Integer> roleIds = userForm.getRoleId();
        roleIds.forEach(roleId->{
            UserRole userRole = new UserRole();
            userRole.setRoleId(roleId);
            userRole.setUserId(id);
            userRoleMapper.insert(userRole);
        });
        return ResponseResult.Success().msg("添加成功");
    }

    @Override
    public ResponseResult updateUser(UserForm userForm) {

        User user = new User();
        BeanUtil.copyProperties(userForm,user);
        //修改用户信息
        baseMapper.updateById(user);

        // 根据用户id删除角色id
        Integer id = userForm.getId();
        LambdaQueryWrapper<UserRole> lambda = new QueryWrapper<UserRole>().lambda();
        lambda.eq(UserRole::getUserId,id);
        userRoleMapper.delete(lambda);

        // 获取roleIds
        List<Integer> roleIds = userForm.getRoleId();
        roleIds.forEach(roleId->{
            UserRole userRole = new UserRole();
            userRole.setUserId(id);
            userRole.setRoleId(roleId);
            userRoleMapper.insert(userRole);
        });

        return ResponseResult.Success().msg("编辑成功");
    }

    @Override
    public ResponseResult deleteById( List<Integer> ids) {
        System.out.println("ids = " + ids);
        // 删除用户信息
        baseMapper.deleteBatchIds(ids);
        // 删除用户角色信息
        LambdaQueryWrapper<UserRole> lambda = new QueryWrapper<UserRole>().lambda();
        lambda.in(UserRole::getUserId,ids);
        userRoleMapper.delete(lambda);
        return ResponseResult.Success().msg("删除成功");
    }

    @Override
    public ResponseResult state(Integer id, Integer state) {
        User user = new User();
        user.setId(id);
        user.setState(state);
        baseMapper.updateById(user);
        return ResponseResult.Success().msg("状态修改成功");
    }

    @Override
    public ResponseResult resetPassword(Integer id) {
        // 重置密码
        String salt = RandomUtil.randomString(6);
        String password= new Md5Hash("111111", salt, 1024).toHex();
        User user = new User();
        user.setPassword(password);
        user.setSalt(salt);
        user.setId(id);
        baseMapper.updateById(user);
        return ResponseResult.Success().msg("密码重置成功");
    }

    @Override
    public ResponseResult menu(HttpServletRequest request) {
        // 获取用户发送请求是请求头携带的token
        String token = request.getHeader("authorization");
        // 获取token中载荷中保存的用户编号"uid"
        Map<String, Object> claims = jwtUtil.getClaims(token);
        Object uid = claims.get("id");
        /**
         * 根据用户编号"uid"，查询用户角色关联表"user_role"
         * 可以得到用户具有的角色编号"roleIds"
         */
        LambdaQueryWrapper<UserRole> userRoleLambda = new QueryWrapper<UserRole>().lambda();
        userRoleLambda.eq(UserRole::getUserId,uid);
        List<Integer> roleIds = userRoleMapper.selectList(userRoleLambda).stream().map(userRole -> userRole.getRoleId()).collect(Collectors.toList());

        /**
         * 根据用户所具有的角色编号"roleIds"，查询角色权限关联表
         * 可以得到用户的权限编号"permissionIds"
         */
        LambdaQueryWrapper<RolePers> rolePermissionLambda = new QueryWrapper<RolePers>().lambda();
        rolePermissionLambda.in(RolePers::getRoleId,roleIds);
        Set<Integer> permissionIds = rolePersMapper.selectList(rolePermissionLambda).stream().map(rolePers -> rolePers.getPerId()).collect(Collectors.toSet());

        /**
         * 根据权限编号"permissionIds"，查询权限表
         * 可以得到用户所具有的权限信息，
         * 因为想要获取的使用户的菜单信息，菜单具有目录和菜单，所以不需要按钮的权限信息
         */
        List<Permission> permissionList = permissionMapper.selectBatchIds(permissionIds)
                .stream()
                .filter(permission -> permission.getIsMenu() != 2)
                .collect(Collectors.toList());
        // 返回响应信息
        return ResponseResult.Success().data("permissionList",permissionList);
    }

    @Override
    public ResponseResult getPerson(HttpServletRequest request) {
        // 获取请求头中的token
        String token = request.getHeader("authorization");
        // 获取载荷中的用户id
        Map<String, Object> claims = jwtUtil.getClaims(token);
        Integer id = (Integer) claims.get("id");
        // 根据用户id查询用户信息
        LambdaQueryWrapper<User> lambda = new QueryWrapper<User>().lambda();
        lambda.eq(User::getId,id)
                .select(User::getId,User::getNickName,User::getSex,User::getCity,User::getTel);
        User user = baseMapper.selectOne(lambda);

        return ResponseResult.Success().data("user",user);
    }

    @Override
    public ResponseResult logout(HttpServletRequest request) {
        // 获取请求头中的token
        String token = request.getHeader("authorization");
        // 对token加密
        String key = MD5.create().digestHex16(token);
        // 删除缓存中的数据
        stringRedisTemplate.delete(key);
        return ResponseResult.Success().msg("退出成功");
    }

    @Override
    public ResponseResult resetPwd(Map<String, String> pwdInfo, HttpServletRequest request) {
        // 获取请求头中的token
        String token = request.getHeader("authorization");
        // 从token的载荷中获取用户id
        Map<String, Object> claims = jwtUtil.getClaims(token);
        Integer id = (Integer) claims.get("id");
        // 根据用户id查询出用户信息
        User user = baseMapper.selectById(id);
        // 获取用户的密码和盐
        String myPwd = user.getPassword();
        // 获取加密的盐
        String salt = user.getSalt();
        // 获取前端的旧密码
        String oldPwd = pwdInfo.get("oldPwd");
        // 对旧密码加密
        String md5pwd = new Md5Hash(oldPwd, salt, 1024).toHex();
        // 比较密码 如果密码不相同，则返回提示信息
        if (!StringUtils.equals(myPwd,md5pwd)){
            return ResponseResult.Fail().msg("原始密码错误");
        }

        // 修改密码
        // 获取新密码
        String newPwd = pwdInfo.get("newPwd");
        String password = new Md5Hash(newPwd, salt, 1024).toHex();
        User userOne = new User();
        userOne.setId(id);
        userOne.setPassword(password);
        baseMapper.updateById(userOne);
        return ResponseResult.Success().msg("密码修改成功");
    }
}




